package com.hong;

import com.hong.Config.ConfigManager;
import com.hong.Config.RpcServerConfig;
import com.hong.config.constant.ServerConfig;
import com.hong.config.factory.ParserFactory;
import com.hong.config.parse.Parser;
import com.hong.factory.PropertiesStrategyFactory;
import com.hong.handler.NettyRequestHandler;
import com.hong.listener.Listener;
import com.hong.listener.ListenerManager;
import com.hong.listener.RpcServerListener;
import com.hong.listener.impl.RpcServerConfigurer;
import com.hong.listener.impl.SpringRegistrar;
import com.hong.qrpc.RequestHandler;
import com.hong.qrpc.TransportServer;
import com.hong.qrpc.codec.Decoder;
import com.hong.qrpc.codec.Encoder;
import com.hong.qrpc.codec.constant.CodecConst;
import com.hong.qrpc.register.Register;
import com.hong.qrpc.utils.ReflectionUtils;
import com.hong.qrpc.utils.StringUtils;
import com.hong.vo.ListenerCandidate;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;

import java.io.IOException;
import java.util.*;

/**
 * @Author HQT
 * @Date 2022/7/10 15:49
 */
@Data
@Slf4j
public class RpcServer implements DisposableBean {
    private ConfigManager configManager = new ConfigManager();
    private TransportServer net;
    private Map<Integer, Encoder> encoders;
    private Map<Integer, Decoder> decoders;
    private ServiceManager serviceManager = new ServiceManager();
    private ServiceInvoke serviceInvoke = new ServiceInvoke();
    private ListenerManager listenerManager = new ListenerManager();
    private Register register;
    private RequestHandler handler = new NettyRequestHandler(this);
    private ConfigurableListableBeanFactory beanFactory;
    /**
     * 由于监听器里添加监听器会导致并发修改问题，所以决定将其缓存在这个队列里，每次调用监听器之前将对列中的监听器加入监听器列表中
     */
    private Queue<ListenerCandidate> listenerCandidates = new LinkedList<>();

    public RpcServer(String profileUrl) throws IOException {
        init(profileUrl);
        afterRpcServerInit();
    }

    //不传url就全部走默认配置或者找默认路径
    public RpcServer() throws IOException {
        String defaultUrl = null;
        if (!StringUtils.isEmpty(defaultUrl = containDefaultProfile())) {
            init(defaultUrl);
        } else {
            init();
        }
        afterRpcServerInit();
    }

    /**
     * 在使用了springboot的项目中的服务端实例初始化
     *
     * @param classes     用于在bean工厂中获取服务实例对象的class
     * @param beanFactory
     * @param profile
     */
    public RpcServer(List<Class> classes, ConfigurableListableBeanFactory beanFactory, Map<Object, Object> profile) throws IOException {
        this.beanFactory = beanFactory;
        init(classes, beanFactory, profile);
        afterRpcServerInit();
    }

    private String containDefaultProfile() {
        try {
            ResourceBundle.getBundle("qrpc.properties");
            return "qrpc.properties";
        } catch (Exception e) {
            return null;
        }
    }

    /**
     * @param classes     需要对外暴露服务的类
     * @param beanFactory bean工厂
     * @param profile     配置文件
     */
    private void init(List<Class> classes, ConfigurableListableBeanFactory beanFactory, Map<Object, Object> profile) {
        init();
        configManager.setProfile(profile);
        SpringRegistrar springRegistrar = new SpringRegistrar(classes, beanFactory);
        for (Class<?> interfazz : springRegistrar.getClass().getInterfaces()) {
            //如果是一个监听器的实现类的话
            if (Listener.class.isAssignableFrom(interfazz)) {
                listenerManager.addListener((Class<? extends Listener>) interfazz, springRegistrar);
            }
        }
        initServerConfigProperties();
    }

    private void init(String profileUrl) {
        init();
        String suffix = profileUrl.substring(profileUrl.lastIndexOf('.'));
        Parser parser = ParserFactory.getParser(suffix);
        //加载配置文件
        configManager.setProfile(parser.parse(profileUrl));
        initServerConfigProperties();
    }

    public void initServerConfigProperties() {
        for (String propertiesName : ReflectionUtils.getAllConfigConstant(ServerConfig.class)) {
            PropertiesStrategyFactory.getPropertiesStrategy(propertiesName).
                    disposeProperties(this);
        }
    }

    private void init() {
        loadSpi();
        loadCustomizationBean();
        RpcServerConfig config = this.configManager.getRpcServerConfig();
        this.net = ReflectionUtils.newInstance(config.getTransportClass());
        this.encoders = CodecConst.getAllTypeEncoder();
        this.decoders = CodecConst.getAllTypeDecoder();
        //推迟net的初始化，因为此时这些配置都未加载完毕
        RpcServerConfigurer.addListener(listenerManager);
    }

    public void start() {
        beforeRpcServerStart();
        synchronized (net) {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    net.start();
                }
            }).start();
            try {
                net.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        afterRpcServerStart();
    }

    public void stop() {
        beforeRpcServerStop();
        net.stop();
        afterRpcServerStop();
    }

    private void afterRpcServerInit() {
        loadCandidateListener();
        HashSet<Listener> listeners = listenerManager.getListenerSets().get(RpcServerListener.class);
        if (listeners != null) {
            for (Listener listener : listeners) {
                ((RpcServerListener) listener).afterRpcServerInit(this);
            }
        }
    }

    private void afterRpcServerStart() {
        loadCandidateListener();
        HashSet<Listener> listeners = listenerManager.getListenerSets().get(RpcServerListener.class);
        if (listeners != null) {
            for (Listener listener : listeners) {
                ((RpcServerListener) listener).afterRpcServerStart(this);
            }
        }
    }

    private void beforeRpcServerStart() {
        loadCandidateListener();
        HashSet<Listener> listeners = listenerManager.getListenerSets().get(RpcServerListener.class);
        if (listeners != null) {
            for (Listener listener : listeners) {
                ((RpcServerListener) listener).beforeRpcServerStart(this);
            }
        }
    }

    private void afterRpcServerStop() {
        loadCandidateListener();
        HashSet<Listener> listeners = listenerManager.getListenerSets().get(RpcServerListener.class);
        if (listeners != null) {
            for (Listener listener : listeners) {
                ((RpcServerListener) listener).afterRpcServerStop(this);
            }
        }
    }

    private void beforeRpcServerStop() {
        loadCandidateListener();
        HashSet<Listener> listeners = listenerManager.getListenerSets().get(RpcServerListener.class);
        if (listeners != null) {
            for (Listener listener : listeners) {
                ((RpcServerListener) listener).beforeRpcServerStop(this);
            }
        }
    }

    private void loadCandidateListener() {
        while (!listenerCandidates.isEmpty()) {
            ListenerCandidate candidate = listenerCandidates.poll();
            listenerManager.addListener(candidate.getListenerClazz(), candidate.getListener());
        }
    }

    public <T> void register(Class<T> interfaceClass, T bean) {
        serviceManager.register(interfaceClass, bean);
    }

    @Override
    public void destroy() throws Exception {
        stop();
    }

    private void loadSpi() {
        for (RpcServerListener listener : ServiceLoader.load(RpcServerListener.class)) {
            listenerManager.addListener(RpcServerListener.class, listener);
        }
    }

    private void loadCustomizationBean() {
        checkAndLoadListener();
    }

    private void checkAndLoadListener() {
        try {
            RpcServerListener bean = beanFactory.getBean(RpcServerListener.class);
            listenerManager.addListener(RpcServerListener.class, bean);
        } catch (Exception e) {
            //not find customization selector
        }
    }
}
